
#ifndef __COMM_TIME_CPP__
#define __COMM_TIME_CPP__

#include "comm/comm_time.h"
#include "comm/comm_dbg_log.h"

#include <ros/ros.h>

#include "time.h"
#include <sys/time.h>
#include <stdio.h>
#include <iostream>

#include <math.h>
#include <string.h>
#include <list>
#include <vector>

using namespace std;

typedef struct {
    int    runFrequency;
    double runPeriod;

    /**************************************************************************************
    功能描述: 根据时长获取运行周期数，时长单位秒
    修改记录:
    **************************************************************************************/
    int GetCycleNum(double timeSec) const
    {
        return (int)((timeSec + 0.5 * runPeriod) * runFrequency);
    }

    /**************************************************************************************
    功能描述: 根据周期数获取时长
    修改记录:
    **************************************************************************************/
    double GetCyclesLen(int cycleNum) const
    {
        return cycleNum * runPeriod;
    }
} RUN_PERIOD_STRU;

RUN_PERIOD_STRU g_objRunPeriod = {1, 1.0};
UPT_MNG_C       g_objUptMng;

/**************************************************************************************
功能描述: 初始化定时器
修改记录:
**************************************************************************************/
INT32 Comm_InitTimer(int runFrequency)
{
    R_ASSERT_1(runFrequency > 0, -1, runFrequency);

    g_objRunPeriod.runFrequency = runFrequency;
    g_objRunPeriod.runPeriod    = 1.0 / (double)runFrequency;

    return 0;
}

/**************************************************************************************
功能描述: 更新定时器
修改记录:
**************************************************************************************/
void Comm_UptTimer(void)
{
    g_objUptMng.UptAll();
}

double Comm_GetTimeSecRos(void)
{
    return ros::Time::now().toSec();
}

/**************************************************************************************
功能描述: 获取时间，用到1970.1.1.0.0.0的秒来计算
修改记录:
**************************************************************************************/
double Comm_GetTimeSec(void)
{
    struct timeval tv;
    gettimeofday(&tv, NULL);

    return double(tv.tv_sec) + double(tv.tv_usec) * 0.000001;
}

/**************************************************************************************
功能描述: 获取系统时间，避免频繁调用
修改记录:
**************************************************************************************/
void Comm_GetTime(ST_TIME_STRU &stTime)
{
    time_t t_time;
    time(&t_time);
    //struct tm *pstTm = gmtime(&t_time);
    struct tm *pstTm = localtime(&t_time);
    if (pstTm == NULL) {
        stTime.year  = 0;
        stTime.month = 0;
        stTime.day   = 0;
        stTime.hour  = 0;
        stTime.min   = 0;
        stTime.sec   = 0;
        return;
    }

    stTime.year  = pstTm->tm_year + 1900;
    stTime.month = pstTm->tm_mon + 1;
    stTime.day   = pstTm->tm_mday;
    stTime.hour  = pstTm->tm_hour;
    stTime.min   = pstTm->tm_min;
    stTime.sec   = pstTm->tm_sec;
}

/**************************************************************************************
功能描述: 获取系统时间字符串，避免频繁调用
修改记录:
**************************************************************************************/
const char *Comm_GetTimeStr(void)
{
    ST_TIME_STRU stTime;
    Comm_GetTime(stTime);

    static char strTime[50];         /* 足够，不用保护，不用年，这个很明显 */
    sprintf(strTime, "%02u%02u-%02u:%02u:%02u", stTime.month, stTime.day, stTime.hour, stTime.min, stTime.sec);

    return strTime;
}

void UPT_MNG_C::Add(UPT_BASE_C *pobjUpt)
{
    if (pobjUpt == NULL) {
        return;
    }

    objMutex_.lock();
    lstUpt_.push_back(pobjUpt);
    ST_LOG_INFO("Obj Reg Count : %u.", UINT32(lstUpt_.size()));
    objMutex_.unlock();
}

void UPT_MNG_C::Rmv(UPT_BASE_C *pobjUpt)
{
    if (pobjUpt == NULL) {
        return;
    }

    objMutex_.lock();
    lstUpt_.remove(pobjUpt);
    ST_LOG_INFO("Obj Reg Count : %u.", UINT32(lstUpt_.size()));
    objMutex_.unlock();
}

void UPT_MNG_C::UptAll(void)
{
    objMutex_.lock();

    for (list<UPT_BASE_C *>::iterator itor = lstUpt_.begin();
         itor != lstUpt_.end();
         itor++) {
        UPT_BASE_C *pobjUpt = (UPT_BASE_C *)(*itor);
        pobjUpt->Upt();
    }

    objMutex_.unlock();
}

TIMER_C::TIMER_C(void)
{
    g_objUptMng.Add(this);

    SetOverTime(0);
    Clear(0);
}

TIMER_C::TIMER_C(int overTimeCycleNum, BOOL isDefaultTimeOut)
{
    g_objUptMng.Add(this);

    SetOverTime(overTimeCycleNum);

    if (isDefaultTimeOut) {
        Clear(0);
    } else {
        Reset();
    }
}

TIMER_C::TIMER_C(double overTime, BOOL isDefaultTimeOut)
{
    g_objUptMng.Add(this);

    SetOverTime(overTime);
    if (isDefaultTimeOut) {
        Clear(0);
    } else {
        Reset();
    }
}

TIMER_C::~TIMER_C(void)
{
    g_objUptMng.Rmv(this);
}

void TIMER_C::SetOverTime(int overTimeCycleNum)
{
    overTime_         = g_objRunPeriod.GetCyclesLen(overTimeCycleNum);
    overTimeCycleNum_ = overTimeCycleNum;
}

void TIMER_C::SetOverTime(double overTime)
{
    overTime_         = overTime;
    overTimeCycleNum_ = g_objRunPeriod.GetCycleNum(overTime);
}

void TIMER_C::Upt(void)
{
    if (leftTimeCycleNum_ > 0) {
        leftTimeCycleNum_--;
    }
}

void TIMER_C::Reset(void)
{
    if (overTimeCycleNum_ == 0) {
        overTimeCycleNum_ = g_objRunPeriod.GetCycleNum(overTime_);
    }

    leftTimeCycleNum_ = overTimeCycleNum_;
}

void TIMER_C::Reset(int overTimeCycleNum)
{
    SetOverTime(overTimeCycleNum);
    Reset();
}

void TIMER_C::Reset(double overTime)
{
    SetOverTime(overTime);
    Reset();
}

void TIMER_C::ResetAndAddTime(double addTime)
{
    int addTimeCycleNum = g_objRunPeriod.GetCycleNum(addTime);

    Reset(addTimeCycleNum + leftTimeCycleNum_);
}

void TIMER_C::Clear(void)
{
    leftTimeCycleNum_ = 0;
}

void TIMER_C::Clear(int leftTimeCycleNum)
{
    if (leftTimeCycleNum_ > leftTimeCycleNum) {
        leftTimeCycleNum_ = leftTimeCycleNum;
    }
}

void TIMER_C::Clear(double leftTime)
{
    int leftTimeCycleNum = g_objRunPeriod.GetCycleNum(leftTime);
    Clear(leftTimeCycleNum);
}

bool TIMER_C::IsTimeOut(void) const
{
    return (leftTimeCycleNum_ <= 0);
}

void CYCLE_LEN_STATIS_C::Set(double outputCycle, double expectedCycleLen, double abnCycleErrRate)
{
    outputCycle_      = outputCycle;
    expectedCycleLen_ = expectedCycleLen;
    abnCycleDiffMax_  = expectedCycleLen * abnCycleErrRate;
}

void CYCLE_LEN_STATIS_C::Add(void)
{
    AddStatis();
    OutputStatis();
}

void CYCLE_LEN_STATIS_C::AddStatis(void)
{
    double curTime = Comm_GetTimeSecRos();
    if (lastCycleTime_ <= 0.0) {
        lastCycleTime_ = curTime;
        return;
    }

    double curCycleLen = curTime - lastCycleTime_;
    lastCycleTime_     = curTime;
    maxCycleLen_       = MAX(maxCycleLen_, curCycleLen);
    minCycleLen_       = MIN(minCycleLen_, curCycleLen);
    totalCycleLen_    += curCycleLen;
    ++statisCount_;

    if (expectedCycleLen_ > 0) {
        double curCycleDiff = ABS(curCycleLen - expectedCycleLen_);
        totalCycleDiff_    += curCycleDiff;

        if (curCycleDiff > abnCycleDiffMax_) {
            ++abnCycleCount_;

            ST_LOG_ERR("Cur Cycle Len(%f) Abnormal.", curCycleLen);
        }
    }
}

void CYCLE_LEN_STATIS_C::ClearStatis(void)
{
    maxCycleLen_    = 0.0;
    minCycleLen_    = 1.0e10;
    totalCycleLen_  = 0.0;
    statisCount_    = 0;
    totalCycleDiff_ = 0.0;
    abnCycleCount_  = 0;
}

void CYCLE_LEN_STATIS_C::OutputStatis(void)
{
    if (totalCycleLen_ < outputCycle_) {
        return;
    }

    R_ASSERT(statisCount_ > 0, NOTHING);

    if (expectedCycleLen_ <= 0.0) {
        ST_LOG_INFO("Count=%u, avgLen=%.4f, minLen=%.4f, maxLen=%.4f.",
                    statisCount_,
                    totalCycleLen_ / double(statisCount_),
                    minCycleLen_,
                    maxCycleLen_);
    } else {
        ST_LOG_INFO("Count=%u, avgLen=%.4f, minLen=%.4f, maxLen=%.4f, abnRate=%.2f, avgErr=%.4f.",
                    statisCount_,
                    totalCycleLen_ / double(statisCount_),
                    minCycleLen_,
                    maxCycleLen_,
                    double(abnCycleCount_) / double(statisCount_),
                    totalCycleDiff_ / double(statisCount_));
    }

    ClearStatis();
}


void PERIOD_TIME_STATIS_C::Start(void)
{
    /* 统计最大起始工作时间间隔 */
    double curTime = Comm_GetTimeSecRos();
    if (startWorkTime_ != 0) {
        double startWorkTimeDiff = curTime - startWorkTime_;
        maxStartTimeDiff_ = MAX(maxStartTimeDiff_, startWorkTimeDiff);
    }

    startWorkTime_ = curTime;
}

void PERIOD_TIME_STATIS_C::Stop(BOOL isSucc)
{
    R_ASSERT_1(startWorkTime_ > 0, NOTHING, startWorkTime_);

    double endTime   = Comm_GetTimeSecRos();

    AddStatis(endTime, isSucc);

    OutputStatis(endTime);
}

BOOL PERIOD_TIME_STATIS_C::IsWorkPeriodTimeout(void)
{
    return ((Comm_GetTimeSecRos() - startWorkTime_) >= workPeriodTime_);
}

void PERIOD_TIME_STATIS_C::Wait(void)
{
    usleep(int(stepSleepTime_ * 1000 * 1000));
}

void PERIOD_TIME_STATIS_C::ClearStatis(void)
{
    periodCount_              = 0;
    periodCountSucc_          = 0;
    succPeriodSpentTimeTotal_ = 0;
    succPeriodSpentTimeMax_   = 0;
    failPeriodSpentTimeMax_   = 0;
    maxStartTimeDiff_         = 0;
}

void PERIOD_TIME_STATIS_C::AddStatis(double curTime, BOOL isSucc)
{
    double spentTime = curTime - startWorkTime_;

    periodCount_++;
    if (isSucc) {
        periodCountSucc_++;
        succPeriodSpentTimeTotal_ += spentTime;
        succPeriodSpentTimeMax_    = MAX(succPeriodSpentTimeMax_, spentTime);
    } else {
        failPeriodSpentTimeMax_    = MAX(failPeriodSpentTimeMax_, spentTime);
    }
}

void PERIOD_TIME_STATIS_C::OutputStatis(double curTime)
{
    /* 1分钟输出一次 */
    if ((curTime - lastLogTime_) > logPeriodTime_) {
        lastLogTime_ = curTime;

        ST_LOG_INFO("[%s] TotalCount=%u, SuccRate=%4.2f, AvgSuccTime=%4.4fs, MaxSuccTime=%4.4fs, MaxFailTime=%4.4fs, maxStartWorkTimeDiff=%4.4fs.",
                    strModule_.c_str(),
                    periodCount_,
                    (periodCount_ == 0) ? 0.0 : (float(periodCountSucc_) / float(periodCount_)),
                    (periodCountSucc_ == 0) ? 0.0 : float(succPeriodSpentTimeTotal_ / float(periodCountSucc_)),
                    float(succPeriodSpentTimeMax_),
                    float(failPeriodSpentTimeMax_),
                    float(maxStartTimeDiff_));

        ClearStatis();
    }
}


#endif


